package com.desay.pangoo.itmt.service.impl;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.IOUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Order;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.web.multipart.MultipartFile;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.desay.pangoo.common.lite.lang.web.BusinessException;
import com.desay.pangoo.common.lite.lang.web.CommonResponse;
import com.desay.pangoo.common.lite.lang.web.PangooWebConstant;
import com.desay.pangoo.common.lite.spring.web.ControllerCommonUtils;
import com.desay.pangoo.itmt.config.TechOnlineReviewSchedule;
import com.desay.pangoo.itmt.constants.TechOnlineReviewFileds;
import com.desay.pangoo.itmt.controller.vo.*;
import com.desay.pangoo.itmt.entity.*;
import com.desay.pangoo.itmt.enums.DataBaseTableEnum;
import com.desay.pangoo.itmt.enums.ExceptionCodeEnum;
import com.desay.pangoo.itmt.enums.OrgRoleEnum;
import com.desay.pangoo.itmt.enums.ReviewTypeEnum;
import com.desay.pangoo.itmt.feign.UserSystemRequestService;
import com.desay.pangoo.itmt.repository.*;
import com.desay.pangoo.itmt.repository.spec.ExpandCriteria;
import com.desay.pangoo.itmt.repository.spec.Restrictions;
import com.desay.pangoo.itmt.service.OrgChartService;
import com.desay.pangoo.itmt.service.TechOnLineReviewService;
import com.desay.pangoo.itmt.utils.CompareObjUtil;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.UUID;
import lombok.extern.slf4j.Slf4j;

/**
 * @author uidq1163
 */
@Service
@Transactional(rollbackFor = Exception.class)
@Slf4j
public class TechOnLineReviewServiceImpl implements TechOnLineReviewService {

	@Autowired
	private WorkGroupRepository workGroupRepository;
	@Autowired
	private TechOnlineReviewFileRepository fileRepository;
	@Autowired
	private TechOnlineReviewRepository techOnlineReviewRepository;
	@Autowired
	private TechOnlineReviewRelevantRepository techOnlineReviewRelevantRepository;
	@Autowired
	private TechOnlineReviewTMTRepository techOnlineReviewTMTRepository;
	@Autowired
	private ModifyLogServiceImpl modifyLogService;
	@Autowired
	private TechOnlineReviewSchedule techOnlineReviewSchedule;
	@Autowired
	private BUDivRepository bUDivRepository;
	@Autowired
	private TechProjectInfoRepository techProjectInfoRepository;
	@Autowired
	private OrgChartService orgChartService;
	@Autowired
	private OrgChartRepository orgChartRepository;

	@Autowired
	private UserSystemRequestService userSystemRequestService;

	@Autowired
	ProjectEntityRepository projectEntityRepository;

	@Value("${itmt.file-root-path}")
	private String fileRootPath;

	private static final String YYYYMMDD = "yyyyMMdd";

	@Override
	public Object uploadFile(MultipartFile[] files, String fileType) throws IOException {
		String user = ControllerCommonUtils.getHeader(PangooWebConstant.HEADER_NAME_USER_FULL_NAME);
		List<TechOnlineReviewFileEntity> list = new ArrayList<>();
		for (MultipartFile file : files) {
			String fileName = file.getOriginalFilename();
			Path dstFilePath = Paths.get(fileRootPath, DateUtil.formatDate(new Date()),
					UUID.fastUUID().toString() + "." + FileUtil.extName(fileName));
			File dstFile = dstFilePath.toFile();
			if (!dstFile.exists()) {
				// 创建父级目录
				FileUtil.mkParentDirs(dstFile);
				file.transferTo(dstFile);
			}
			TechOnlineReviewFileEntity fileEntity = new TechOnlineReviewFileEntity();
			fileEntity.setFilePath(dstFilePath.toString());
			fileEntity.setFileName(fileName);
			fileEntity.setFileType(fileType);
			fileEntity.setCreator(user);
			list.add(fileEntity);
		}
		fileRepository.saveAll(list);
		// 返回文件id
		return list.stream().map(f -> {
			Map<String, Object> map = new HashMap<>(3);
			map.put("id", f.getId());
			map.put("fullPath", f.getFilePath());
			map.put("fileName", f.getFileName());
			return map;
		}).toList();
	}

	@Override
	public void deleteFile(Long id) {
		Optional<TechOnlineReviewFileEntity> opiton = fileRepository.findById(id);
		if (!opiton.isPresent()) {
			throw new BusinessException(ExceptionCodeEnum.UPLOAD_FILE_NOT_EXIST);
		}
		TechOnlineReviewFileEntity file = opiton.get();
		String filePath = file.getFilePath();
		// 删除关联信息
		fileRepository.delete(file);
		// 删除文件
		if (FileUtil.exist(filePath)) {
			FileUtil.del(filePath);
		}
	}

	/**
	 * 下载文件
	 *
	 * @param fileEntity
	 * @param resp
	 */
	private void downloadFile(TechOnlineReviewFileEntity fileEntity, HttpServletResponse resp) {
		File file = new File(fileEntity.getFilePath());
		resp.setContentType("application/octet-stream");
		try {
			resp.setHeader("Content-Disposition",
					String.format("attachment; filename=\"%s\"", URLEncoder.encode(fileEntity.getFileName(), "UTF-8")));
		} catch (Exception e1) {
			e1.printStackTrace();
		}
		resp.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
		resp.setHeader("Pragma", "no-cache");
		resp.setHeader("Expires", "0");
		try (InputStream in = new FileInputStream(file);) {
			resp.setHeader(HttpHeaders.CONTENT_LENGTH, file.length() + "");
			IOUtils.copyLarge(in, resp.getOutputStream());
			resp.flushBuffer();
			resp.getOutputStream().close();
		} catch (Exception e) {
			log.error("", e);
		}
	}

	@Override
	public void downloadFile(Long id, HttpServletResponse resp) {
		Optional<TechOnlineReviewFileEntity> opiton = fileRepository.findById(id);
		if (!opiton.isPresent()) {
			throw new BusinessException(ExceptionCodeEnum.UPLOAD_FILE_NOT_EXIST);
		}
		downloadFile(opiton.get(), resp);
	}

	@Override
	public Object getFileList(String fileType) {
		ExpandCriteria<TechOnlineReviewFileEntity> c = new ExpandCriteria<>();
		c.add(Restrictions.eq("fileType", fileType, true));
		Order order = new Order(Sort.Direction.DESC, "createTime");
		Pageable pageReq = PageRequest.of(0, 1, Sort.by(order));
		return fileRepository.findAll(c, pageReq);
	}

	@Override
	public Object create(TechOnlineReviewVo vo) {
		String user = ControllerCommonUtils.getHeader(PangooWebConstant.HEADER_NAME_USER_FULL_NAME);
		TechOnlineReviewEntity techOnlineReviewEntity = new TechOnlineReviewEntity();
		TechOnlineReviewEntity relatedReviewEntity = new TechOnlineReviewEntity();
		BeanUtils.copyProperties(vo, techOnlineReviewEntity, "id");
		// 检查工作组是否存在
		if (null != vo.getWorkGroupId()) {
			Optional<WorkGroupEntity> option = workGroupRepository.findById(vo.getWorkGroupId());
			if (option.isPresent()) {
				techOnlineReviewEntity.setWorkGroup(option.get());
			} else {
				throw new BusinessException(ExceptionCodeEnum.WORK_GROUP_NOT_EXIST);
			}
		} else {
			throw new BusinessException(ExceptionCodeEnum.WORK_GROUP_NOT_EMPTY);
		}

		// 检查工作组是否存在
		if (null != vo.getCrossBreedWorkGroupId()) {
			Optional<WorkGroupEntity> option = workGroupRepository.findById(vo.getCrossBreedWorkGroupId());
			if (option.isPresent()) {
				techOnlineReviewEntity.setCrossBreedWorkGroup(option.get());
			} else {
				throw new BusinessException(ExceptionCodeEnum.WORK_GROUP_NOT_EXIST);
			}
		}

		// 新建评审流程：项目名称，子项目允许重复
		if (null == vo.getParentId()) {
			ExpandCriteria<TechOnlineReviewEntity> c = new ExpandCriteria<>();
			c.add(Restrictions.eq(TechOnlineReviewFileds.PROPOSALNAME, vo.getProposalName(), true));
			c.add(Restrictions.eq(TechOnlineReviewFileds.SUBSIDIARY, vo.getSubsidiary(), true));
			c.add(Restrictions.eq(TechOnlineReviewFileds.WORKGROUP, vo.getWorkGroupId(), true));
			c.add(Restrictions.eq(TechOnlineReviewFileds.REVIEWTYPE, vo.getReviewType(), true));
			List<TechOnlineReviewEntity> listExist = techOnlineReviewRepository.findAll(c);
			if (CollUtil.isNotEmpty(listExist)) {
				throw new BusinessException(ExceptionCodeEnum.TECH_ONLINE_REVIEW_INFO_EXIST);
			}
		} else {
			// 是否多次评审：关联上次评审的ID
			Optional<TechOnlineReviewEntity> option = techOnlineReviewRepository.findById(vo.getParentId());
			if (option.isPresent()) {
				techOnlineReviewEntity.setParent(option.get());
			} else {
				throw new BusinessException(ExceptionCodeEnum.TECH_ONLINE_REVIEW_NOT_EXIST);
			}
		}
		if (CollUtil.isNotEmpty(vo.getFileIds())) {
			Iterable<TechOnlineReviewFileEntity> allFiles = fileRepository.findAllById(vo.getFileIds());
			Set<TechOnlineReviewFileEntity> list = new HashSet<>();
			allFiles.forEach(list::add);
			techOnlineReviewEntity.setFiles(list);
		}
		techOnlineReviewEntity.setCreator(user);
		// 开发费用归属
		ExpandCriteria<BUDivEntity> buc = new ExpandCriteria<>();
		if (CollUtil.isNotEmpty(vo.getCostBuss())) {
			buc.add(Restrictions.in("id", vo.getCostBuss(), true));
			List<BUDivEntity> bus = bUDivRepository.findAll(buc);
			techOnlineReviewEntity.setCostBuss(bus.stream().collect(Collectors.toSet()));
		}
		// 开发责任主体
		if (CollUtil.isNotEmpty(vo.getDutyBuss())) {
			buc = new ExpandCriteria<>();
			buc.add(Restrictions.in("id", vo.getDutyBuss(), true));
			List<BUDivEntity> bus = bUDivRepository.findAll(buc);
			techOnlineReviewEntity.setDutyBuss(bus.stream().collect(Collectors.toSet()));
		}
		// 技术定义与研究任务书提案名称
		// 技术点信息
		if (CollUtil.isNotEmpty(vo.getTechIds())) {
			ExpandCriteria<TechProjectInfoEntity> project = new ExpandCriteria<>();
			project.add(Restrictions.in("id", vo.getTechIds(), true));
			techOnlineReviewEntity
					.setTechProjects(techProjectInfoRepository.findAll(project).stream().collect(Collectors.toSet()));
		}
		// 技术交付环节关联提案
		if (null != vo.getProjectId()) {
			techOnlineReviewEntity.setRelatedReviewEntityId(vo.getProjectId());
			Optional<TechOnlineReviewEntity> option = techOnlineReviewRepository.findById(vo.getProjectId());
			if (option.isPresent()) {
				relatedReviewEntity = option.get();
			} else {
				throw new BusinessException(ExceptionCodeEnum.TECH_ONLINE_REVIEW_NOT_EXIST);
			}
			if (relatedReviewEntity.getReviewType() == ReviewTypeEnum.TECH_BU.getType()) {
				techOnlineReviewEntity.setIsPlReview(Boolean.TRUE);
			}
		}
		if (CollUtil.isNotEmpty(vo.getResponsibles())) {
			// 业务负责人
			techOnlineReviewEntity.setResponsible(String.join(",", vo.getResponsibles()));
		}
		if (vo.getReviewType().equals(ReviewTypeEnum.TECH_BU.getType())
				|| Boolean.TRUE.equals(techOnlineReviewEntity.getIsPlReview())) {
			// 提案归属业务模块
			// BU TMT批准技术课题
			techOnlineReviewEntity.setTmtReviewDueDate(vo.getReviewDate());
			// 设置PLTMT Leader评审截止日期
			techOnlineReviewEntity.setDueDate(vo.getReviewDate());
			//PLITMT Leader提案注册和技术交付注册时选择类型4同样需要pl评审
			techOnlineReviewEntity.setIsPlReview(true);
		} else {
			// TMT相关性反馈期限（当前日期+2个工作日）->修改为N+1
			ResponseEntity<CommonResponse<Object>> response = userSystemRequestService
					.getWorkDayDiff(DateUtil.format(new Date(), YYYYMMDD), 3);
			String reviewDate = "";
			if (response.getStatusCodeValue() == HttpStatus.OK.value() && Objects.nonNull(response.getBody())) {
				reviewDate = (String) response.getBody().getData();
			}
			techOnlineReviewEntity.setTmtReviewDueDate(DateUtil.parse(reviewDate, YYYYMMDD));
			// 流程结束期限（评审会时间+1个工作日）
			response = userSystemRequestService.getWorkDayDiff(DateUtil.format(vo.getReviewDate(), YYYYMMDD), 1);
			if (response.getStatusCodeValue() == HttpStatus.OK.value() && Objects.nonNull(response.getBody())) {
				reviewDate = (String) response.getBody().getData();
			}
			techOnlineReviewEntity.setDueDate(DateUtil.parse(reviewDate, YYYYMMDD));
			// 开启相关性反馈
			techOnlineReviewEntity.setOpenRelevant(true);
		}
		if (null == vo.getParentId()) {
			if (Boolean.TRUE.equals(techOnlineReviewEntity.getIsPlReview())) {
				//关闭评审会和相关性反馈
				techOnlineReviewEntity.setOpenReview(false);
				techOnlineReviewEntity.setOpenRelevant(false);
				TechOnlineReviewEntity finalTechOnlineReviewEntity = techOnlineReviewRepository
						.save(techOnlineReviewEntity);
				// 责任人
				List<OrgChartEntity> responsibleList = orgChartRepository.findByLoginInAndOrgRole(vo.getResponsibles(),
						OrgRoleEnum.PLTMT_Leader.getValue());
				// responsibleList 去重
				responsibleList = responsibleList.stream().distinct().collect(Collectors.toList());
				List<String> pltmts = responsibleList.stream().map(item -> {
					TechOnlineReviewTMTEntity entity = new TechOnlineReviewTMTEntity();
					entity.setCreator(user);
					entity.setTechOnlineReview(finalTechOnlineReviewEntity);
					entity.setTmtUser(item.getLogin());
					entity.setTmtMail(item.getEmail());
					entity.setTmtFullUser(item.getNickName());
					entity.setIsParticipate(true);
					entity.setJudgesType(1);
					entity.setIsPL(1);
					techOnlineReviewTMTRepository.save(entity);
					return entity.getTmtUser();
				}).toList();
				// 发消息通知PLTMT
				// 发送通知给出席会议的评委
				if (CollUtil.isNotEmpty(pltmts)) {
					techOnlineReviewSchedule.pushMessageToTmt(1, finalTechOnlineReviewEntity, pltmts);
				}
				// 将TMT对象保存
				List<OrgChartEntity> responsibleListTMT = orgChartRepository
						.findByOrgRole(OrgRoleEnum.ITMT_Leader.getValue());
				List<String> tmts = responsibleListTMT.stream().map(item -> {
					TechOnlineReviewTMTEntity tmtEntity = new TechOnlineReviewTMTEntity();
					tmtEntity.setCreator(user);
					tmtEntity.setTechOnlineReview(finalTechOnlineReviewEntity);
					tmtEntity.setTmtUser(item.getLogin());
					tmtEntity.setTmtMail(item.getEmail());
					tmtEntity.setTmtFullUser(item.getNickName());
					tmtEntity.setIsParticipate(true);
					tmtEntity.setJudgesType(1);
					tmtEntity.setIsPL(0);
					techOnlineReviewTMTRepository.save(tmtEntity);
					return tmtEntity.getTmtUser();
				}).toList();
			} else if (vo.getReviewType() == 3 && CollUtil.isNotEmpty(vo.getRelevants())) {
				// 技术成果物交付：如果有评审信息，就不开启相关性反馈
				techOnlineReviewEntity.setOpenRelevant(false);
				techOnlineReviewEntity = techOnlineReviewRepository.save(techOnlineReviewEntity);
				// 自动添加相关性反馈信息
				relevant(vo.getRelevants(), techOnlineReviewEntity);
				// 查找所有相关反馈列表
				List<TechOnlineReviewRelevantEntity> relevantList = techOnlineReviewRelevantRepository
						.findByTechOnlineReview(techOnlineReviewEntity);
				// 推送消息给提案人，参会人员名单
				techOnlineReviewSchedule.pushListOfParticipantsByFeishu(null, "提案参会人员名单", techOnlineReviewEntity,
						relevantList);
				// 创建评审会日历信息
				try {
					techOnlineReviewSchedule.createCalendars(null, techOnlineReviewEntity);
					List<TechOnlineReviewTMTEntity> listTmts = techOnlineReviewTMTRepository
							.findByTechOnlineReview(techOnlineReviewEntity);
					// 添加参会人员到日程信息
					List<String> ids = listTmts.stream().map(TechOnlineReviewTMTEntity::getTmtUser).toList();
					techOnlineReviewSchedule.updateCalendars(techOnlineReviewEntity.getEventId(), null, ids);
				} catch (Exception e) {
					log.error("---创建评审会日历信息--{}", e);
				}
			} else {
				techOnlineReviewEntity = techOnlineReviewRepository.save(techOnlineReviewEntity);
				// 邮件通知ITMT成员参与反馈
				techOnlineReviewSchedule.technicalProposal(techOnlineReviewEntity);
			}
		} else {
			// 多次评审
			Optional<TechOnlineReviewEntity> parentOptional = techOnlineReviewRepository.findById(vo.getParentId());
			if (parentOptional.isPresent()) {
				// 二次评审评委参与情况反馈期限（当前日期+1个工作日）
				ResponseEntity<CommonResponse<Object>> response = userSystemRequestService
						.getWorkDayDiff(DateUtil.format(new Date(), YYYYMMDD), 1);
				String reviewDate = "";
				if (response.getStatusCodeValue() == HttpStatus.OK.value() && Objects.nonNull(response.getBody())) {
					reviewDate = (String) response.getBody().getData();
				}
				techOnlineReviewEntity.setTmtReviewDueDate(DateUtil.parse(reviewDate, YYYYMMDD));
				TechOnlineReviewEntity parent = parentOptional.get();
				techOnlineReviewEntity.setParent(parent);
				// 不开启ITMT Leader评审
				techOnlineReviewEntity.setOpenRelevant(null);

				TechOnlineReviewEntity result = techOnlineReviewRepository.save(techOnlineReviewEntity);
				// 上一次评审：ITMT Leader相关性评审信息
				List<TechOnlineReviewRelevantEntity> parentRelevantList = techOnlineReviewRelevantRepository
						.findByTechOnlineReview(parent);
				List<TechOnlineReviewRelevantEntity> relevantList = new ArrayList<>();
				parentRelevantList.stream().forEach(p -> {
					TechOnlineReviewRelevantEntity entity = new TechOnlineReviewRelevantEntity();
					BeanUtils.copyProperties(p, entity, "id");
					entity.setTechOnlineReview(result);
					// 获取所有TMT成员
					Set<TechOnlineReviewTMTEntity> tmtUsers = p.getTmtInfos().stream().map(t -> {
						TechOnlineReviewTMTEntity tmtEntity = new TechOnlineReviewTMTEntity();
						// 评委类型
						tmtEntity.setJudgesType(t.getJudgesType());
						tmtEntity.setTmtFullUser(t.getTmtFullUser());
						tmtEntity.setTmtMail(t.getTmtMail());
						tmtEntity.setTmtUser(t.getTmtUser());
						tmtEntity.setWorkGroup(t.getWorkGroup());
						tmtEntity.setTechOnlineReview(result);
						// 评审不通过的人员强制参会
						if (Boolean.FALSE.equals(t.getIsPassed())) {
							tmtEntity.setIsParticipate(true);
						}
						tmtEntity.setIsPassed(t.getIsPassed());
						return tmtEntity;
					}).collect(Collectors.toSet());
					// 保存TMT评审信息
					techOnlineReviewTMTRepository.saveAll(tmtUsers);
					entity.setTmtInfos(tmtUsers);
					techOnlineReviewRelevantRepository.save(entity);
					relevantList.add(entity);
				});
				// 系统向提案人发送二次评审参会人员名单
				// 二次评审，不需要开启ITMT Leader 相关性反馈，直接向一次评委清单人员发送通知
				List<String> tmts = new ArrayList<>();
				// 通知所有需要参与人员
				Iterable<TechOnlineReviewTMTEntity> judgeslist = techOnlineReviewTMTRepository
						.findByTechOnlineReviewAndJudgesType(result, 1);
				judgeslist.forEach(t -> tmts.add(t.getTmtUser()));
				// 向一次评委清单人员发送通知
				// 发送通知给出席会议的评委
				techOnlineReviewSchedule.pushMessageToTmt(1, result, tmts);
				// 创建评审会日历信息
				try {
					techOnlineReviewSchedule.createCalendars(null, result);
					List<TechOnlineReviewTMTEntity> listTmts = techOnlineReviewTMTRepository
							.findByTechOnlineReview(result);
					// 添加参会人员到日程信息
					List<String> ids = listTmts.stream().map(TechOnlineReviewTMTEntity::getTmtUser).toList();
					techOnlineReviewSchedule.updateCalendars(result.getEventId(), null, ids);
				} catch (Exception e) {
					log.error("---创建评审会日历信息--{}", e);
				}
			} else {
				throw new BusinessException(ExceptionCodeEnum.TECH_ONLINE_REVIEW_NOT_EXIST);
			}
		}

		return techOnlineReviewEntity.getId();
	}

	/**
	 * 技术成果物交付评审：自动添加评委信息
	 *
	 * @param relevants
	 * @param reviewEntity
	 */
	private void relevant(List<TechOnlineReviewRelevantVo2> relevants, TechOnlineReviewEntity reviewEntity) {
		TechOnlineReviewRelevantEntity techOnlineReviewRelevantEntity = null;
		for (TechOnlineReviewRelevantVo2 vo : relevants) {
			WorkGroupEntity workGroup = null;
			techOnlineReviewRelevantEntity = new TechOnlineReviewRelevantEntity();
			techOnlineReviewRelevantEntity.setIsRelevant(vo.getIsRelevant());
			techOnlineReviewRelevantEntity.setDescription(vo.getDescription());
			// 添加旁听或者正式属性
			techOnlineReviewRelevantEntity.setAuditOrNot(vo.getAuditOrNot());
			techOnlineReviewRelevantEntity.setTechOnlineReview(reviewEntity);
			// 属于哪个域
			if (!TechOnlineReviewFileds.PRG_WORKGROUP_ID.equals(vo.getWorkGroupId())) {
				Optional<WorkGroupEntity> workGroupOptional = workGroupRepository.findById(vo.getWorkGroupId());
				if (workGroupOptional.isPresent()) {
					workGroup = workGroupOptional.get();
					// 获取所在域的leader
					techOnlineReviewRelevantEntity.setWorkGroup(workGroup);
					OrgChartEntity leader = orgChartRepository.findByWorkGroupAndOrgRole(workGroup, 2);
					if (null != leader) {
						techOnlineReviewRelevantEntity.setCreator(leader.getLogin());
						techOnlineReviewRelevantEntity.setCreatorName(leader.getNickName());
					}
				} else {
					throw new BusinessException(ExceptionCodeEnum.WORK_GROUP_NOT_EXIST);
				}
			} else {
				techOnlineReviewRelevantEntity.setCreator(vo.getCreator());
				techOnlineReviewRelevantEntity.setCreatorName(vo.getCreatorName());
			}
			// 当前课题与所在域相关
			List<TechOnlineReviewTMTVo> tmts = vo.getTmtInfos();
			if (CollUtil.isNotEmpty(tmts)) {
				// 新增的数据，抹掉从其他地方带过来的id
				tmts.forEach(t -> {
					if (null == t.getJudgesType()) {
						if (Boolean.TRUE.equals(vo.getIsRelevant())) {
							t.setJudgesType(1);
						}
						if (Boolean.TRUE.equals(vo.getAuditOrNot())) {
							t.setJudgesType(2);
						}
					}
					t.setId(null);
				});
				if (Boolean.TRUE.equals(vo.getIsRelevant())) {
					// 添加TMT人员
					addTmtReview(tmts, techOnlineReviewRelevantEntity, workGroup);
				}
				// 添加旁听人员
				if (Boolean.TRUE.equals(vo.getAuditOrNot())) {
					// 添加TMT人员
					addTmtReview(tmts, techOnlineReviewRelevantEntity, workGroup);
				}
			}
			techOnlineReviewRelevantRepository.save(techOnlineReviewRelevantEntity);
		}
	}

	@Override
	public void update(Long id, TechOnlineReviewVo vo) {
		String user = ControllerCommonUtils.getHeader(PangooWebConstant.HEADER_NAME_USER_FULL_NAME);
		Optional<TechOnlineReviewEntity> option = techOnlineReviewRepository.findById(id);
		if (option.isPresent()) {
			TechOnlineReviewEntity techOnlineReviewEntity = option.get();
			if (null == techOnlineReviewEntity.getParent()) {
				// 项目名称，子项目不能重复
				ExpandCriteria<TechOnlineReviewEntity> c = new ExpandCriteria<>();
				c.add(Restrictions.ne("id", id, true));
				c.add(Restrictions.eq(TechOnlineReviewFileds.PROPOSALNAME, vo.getProposalName(), true));
				c.add(Restrictions.eq(TechOnlineReviewFileds.SUBSIDIARY, vo.getSubsidiary(), true));
				c.add(Restrictions.eq(TechOnlineReviewFileds.WORKGROUP, vo.getWorkGroupId(), true));
				c.add(Restrictions.eq(TechOnlineReviewFileds.REVIEWTYPE, vo.getReviewType(), true));
				List<TechOnlineReviewEntity> listExist = techOnlineReviewRepository.findAll(c);
				if (CollUtil.isNotEmpty(listExist)) {
					throw new BusinessException(ExceptionCodeEnum.TECH_ONLINE_REVIEW_INFO_EXIST);
				}
			}

			if (!vo.getVersion().equals(techOnlineReviewEntity.getVersion())) {
				throw new BusinessException(ExceptionCodeEnum.DATA_VERSION_ERROR);
			}
			TechOnlineReviewEntity oldEntity = CompareObjUtil.clone(techOnlineReviewEntity);

			BeanUtils.copyProperties(vo, techOnlineReviewEntity);

			// 判断是否更新评审会时间
			if (null != vo.getReviewDate()) {
				// 流程结束期限（评审会时间+1个工作日）
				ResponseEntity<CommonResponse<Object>> response = userSystemRequestService
						.getWorkDayDiff(DateUtil.format(vo.getReviewDate(), YYYYMMDD), 1);
				String reviewDate = "";
				if (response.getStatusCodeValue() == HttpStatus.OK.value()) {
					reviewDate = (String) response.getBody().getData();
				}
				techOnlineReviewEntity.setDueDate(DateUtil.parse(reviewDate, YYYYMMDD));
			}

			// 附件信息
			// 取出所有关联文件ID
			List<Long> ids = techOnlineReviewEntity.getFiles().stream().map(t -> t.getId())
					.collect(Collectors.toList());
			if (CollUtil.isNotEmpty(vo.getFileIds())) {
				ids.removeAll(vo.getFileIds());
				// 关联附件文件信息
				Iterable<TechOnlineReviewFileEntity> allFiles = fileRepository.findAllById(vo.getFileIds());
				Set<TechOnlineReviewFileEntity> list = new HashSet<>();
				allFiles.forEach(list::add);
				techOnlineReviewEntity.setFiles(list);
			} else {
				techOnlineReviewEntity.setFiles(null);
			}
			if (CollUtil.isNotEmpty(ids)) {
				// 删除多余的ID及文件
				ids.forEach(this::deleteFile);
			}
			ExpandCriteria<BUDivEntity> buc = new ExpandCriteria<>();
			if (CollUtil.isNotEmpty(vo.getCostBuss())) {
				buc.add(Restrictions.in("id", vo.getCostBuss(), true));
				techOnlineReviewEntity.setCostBuss(bUDivRepository.findAll(buc).stream().collect(Collectors.toSet()));
			} else {
				techOnlineReviewEntity.setCostBuss(null);
			}
			if (CollUtil.isNotEmpty(vo.getDutyBuss())) {
				buc = new ExpandCriteria<>();
				buc.add(Restrictions.in("id", vo.getDutyBuss(), true));
				techOnlineReviewEntity.setDutyBuss(bUDivRepository.findAll(buc).stream().collect(Collectors.toSet()));
			} else {
				techOnlineReviewEntity.setDutyBuss(null);
			}
			techOnlineReviewEntity.setUpdator(user);
			// 修改工作组：检查工作组是否存在
			if (null != vo.getWorkGroupId()) {
				Optional<WorkGroupEntity> workGroupOption = workGroupRepository.findById(vo.getWorkGroupId());
				if (workGroupOption.isPresent()) {
					techOnlineReviewEntity.setWorkGroup(workGroupOption.get());
				} else {
					throw new BusinessException(ExceptionCodeEnum.WORK_GROUP_NOT_EXIST);
				}
			} else {
				throw new BusinessException(ExceptionCodeEnum.WORK_GROUP_NOT_EMPTY);
			}
			techOnlineReviewRepository.save(techOnlineReviewEntity);
			try {
				TechOnlineReviewEntity newEntity = CompareObjUtil.clone(techOnlineReviewEntity);
				if (null != oldEntity && null != newEntity) {
					// 保存修改日志
					// 不需要比较的字段（分类）
					newEntity.setTechProjects(null);
					oldEntity.setTechProjects(null);
					newEntity.setParent(null);
					oldEntity.setParent(null);
					newEntity.setChildren(null);
					oldEntity.setChildren(null);
					if (CollUtil.isNotEmpty(newEntity.getFiles())) {
						newEntity.getFiles().forEach(a -> a.setTechOnlineReview(null));
					}
					if (CollUtil.isNotEmpty(oldEntity.getFiles())) {
						oldEntity.getFiles().forEach(a -> a.setTechOnlineReview(null));
					}
					List<?> diffs = CompareObjUtil.compareObj(oldEntity, newEntity);
					if (CollUtil.isNotEmpty(diffs)) {
						ModifyLogEntity modifyLogEntity = new ModifyLogEntity();
						modifyLogEntity.setAssociatedId(id);
						modifyLogEntity.setAssociatedTable(DataBaseTableEnum.TECH_ONLINE_REVIEW.getName());
						modifyLogEntity.setDetails(JSON.toJSONString(diffs, SerializerFeature.WriteMapNullValue));
						modifyLogService.saveLog(modifyLogEntity);
					}
				}
			} catch (Exception e) {
				log.error("写入数据集log失败：{}", e);
			}
		} else {
			throw new BusinessException(ExceptionCodeEnum.TECH_ONLINE_REVIEW_NOT_EXIST);
		}
	}

	@Override
	public Object getInfoById(Long id) {
		Optional<TechOnlineReviewEntity> option = techOnlineReviewRepository.findById(id);
		if (option.isPresent()) {
			List<ProjectEntity> list = projectEntityRepository.findByTechReviewId(id);
			TechOnlineReviewEntity entity = option.get();
			entity.setProjectList(list);
			return entity;
		} else {
			throw new BusinessException(ExceptionCodeEnum.TECH_ONLINE_REVIEW_NOT_EXIST);
		}
	}

	@Override
	public Object relevant(Boolean isAdmin, TechOnlineReviewRelevantVo vo) {
		String user = ControllerCommonUtils.getHeader(PangooWebConstant.HEADER_NAME_USER_FULL_NAME);
		String userId = ControllerCommonUtils.getHeader(PangooWebConstant.HEADER_NAME_USER_ID);

		Boolean itmtFlg = false;
		List<String> leaders = techOnlineReviewSchedule.getUserGroupLoginInfo(TechOnlineReviewSchedule.ITMT_LEADER);
		List<String> subLeaders = techOnlineReviewSchedule
				.getUserGroupLoginInfo(TechOnlineReviewSchedule.ITMT_SUB_LEADER);
		// ITMT Leader 或者ITMT 副Leader可以参与相关性反馈
		if ((CollUtil.isNotEmpty(leaders) && leaders.contains(userId))
				|| (CollUtil.isNotEmpty(subLeaders) && subLeaders.contains(userId))) {
			itmtFlg = true;
		}
		List<String> tsps = techOnlineReviewSchedule.getUserGroupLoginInfo(TechOnlineReviewSchedule.ITMT_TSP);
		List<String> prgs = techOnlineReviewSchedule.getUserGroupLoginInfo(TechOnlineReviewSchedule.ITMT_PRG);
		// TSP或者PRG也可以参与相关性反馈
		if (CollUtil.isNotEmpty(tsps) && tsps.contains(userId)) {
			itmtFlg = true;
		}
		if (CollUtil.isNotEmpty(prgs) && prgs.contains(userId)) {
			itmtFlg = true;
		}
		// 您不是ITMT成员或者TSP成员，不能反馈
		if (Boolean.FALSE.equals(itmtFlg)) {
			throw new BusinessException(ExceptionCodeEnum.TECH_ONLINE_NOT_ITMT);
		}
		Optional<TechOnlineReviewEntity> option = techOnlineReviewRepository.findById(vo.getTechOnlineReviewId());
		TechOnlineReviewEntity reviewEntity = null;
		if (option.isPresent()) {
			reviewEntity = option.get();
		} else {
			throw new BusinessException(ExceptionCodeEnum.TECH_ONLINE_REVIEW_NOT_EXIST);
		}
		// 管理员：不需要验证通道是否开启
		if (Boolean.FALSE.equals(isAdmin) && Boolean.FALSE.equals(reviewEntity.getOpenRelevant())) {
			// 相关性反馈通道已经关闭，不能继续评审（ITMT Leader）
			throw new BusinessException(ExceptionCodeEnum.TECH_ONLINE_ITMT_OVER_DUE_DATE);
		}
		// 是否已经提交过相关性反馈
		ExpandCriteria<TechOnlineReviewRelevantEntity> c = new ExpandCriteria<>();
		// c.add(Restrictions.eq("creator", userId, true));
		c.add(Restrictions.eq("techOnlineReview.id", vo.getTechOnlineReviewId(), true));
		if (Objects.nonNull(vo.getWorkGroupId())
				&& !TechOnlineReviewFileds.PRG_WORKGROUP_ID.equals(vo.getWorkGroupId())) {
			c.add(Restrictions.eq("workGroup.id", vo.getWorkGroupId(), true));
		}
		List<TechOnlineReviewRelevantEntity> listExist = techOnlineReviewRelevantRepository.findAll(c);
		TechOnlineReviewRelevantEntity techOnlineReviewRelevantEntity = null;
		if (CollUtil.isNotEmpty(listExist)) {
			techOnlineReviewRelevantEntity = listExist.get(0);
			techOnlineReviewRelevantEntity.setUpdator(user);
		} else {
			techOnlineReviewRelevantEntity = new TechOnlineReviewRelevantEntity();
			techOnlineReviewRelevantEntity.setTechOnlineReview(reviewEntity);
			techOnlineReviewRelevantEntity.setCreator(userId);
			techOnlineReviewRelevantEntity.setCreatorName(user);
		}
		techOnlineReviewRelevantEntity.setIsRelevant(vo.getIsRelevant());
		techOnlineReviewRelevantEntity.setDescription(vo.getDescription());
		// 添加旁听或者正式属性
		techOnlineReviewRelevantEntity.setAuditOrNot(vo.getAuditOrNot());
		// TSP、PRG不需要判断是否属于哪个域
		WorkGroupEntity workGroup = null;
		if (null != vo.getWorkGroupId() && !TechOnlineReviewFileds.PRG_WORKGROUP_ID.equals(vo.getWorkGroupId())) {
			// 判读当前ITMT leader 属于哪个域
			Optional<WorkGroupEntity> workGroupOptional = workGroupRepository.findById(vo.getWorkGroupId());
			if (workGroupOptional.isPresent()) {
				workGroup = workGroupOptional.get();
				techOnlineReviewRelevantEntity.setWorkGroup(workGroup);
			} else {
				throw new BusinessException(ExceptionCodeEnum.TECH_ONLINE_NOT_ITMT);
			}
		} else {
			if (!tsps.contains(userId) && !prgs.contains(userId)) {
				throw new BusinessException(ExceptionCodeEnum.TECH_ONLINE_NOT_ITMT);
			}
		}
		// 当前课题与所在域相关
		List<TechOnlineReviewTMTVo> tmts = vo.getTmts();
		if (Boolean.TRUE.equals(vo.getIsRelevant())) {
			// 添加TMT人员
			addTmtReview(tmts, techOnlineReviewRelevantEntity, workGroup);
		}
		// 添加旁听人员
		if (Boolean.TRUE.equals(vo.getAuditOrNot())) {
			// 添加TMT人员
			addTmtReview(tmts, techOnlineReviewRelevantEntity, workGroup);
		}
		// 1、如果申请时选择的【工作组】在反馈中表示【不相关】，则评审流程结束且结论为【不通过】
		if (Boolean.FALSE.equals(techOnlineReviewRelevantEntity.getIsRelevant())) {
			if (null != workGroup && workGroup.getId().equals(reviewEntity.getWorkGroup().getId())) {
				// 则评审流程结束且结论为【不通过】
				reviewEntity.setReviewResult(false);
				reviewEntity.setUpdator(user);
				techOnlineReviewRepository.save(reviewEntity);
			}
		}
		techOnlineReviewRelevantRepository.save(techOnlineReviewRelevantEntity);

		// 添加参会人员到日程信息
		List<String> ids = tmts.stream().map(TechOnlineReviewTMTVo::getTmtUser).toList();
		techOnlineReviewSchedule.updateCalendars(reviewEntity.getEventId(), null, ids);

		return null;
	}

	@Override
	public Object openView(Long id) {
		String user = ControllerCommonUtils.getHeader(PangooWebConstant.HEADER_NAME_USER_FULL_NAME);
		Optional<TechOnlineReviewEntity> option = techOnlineReviewRepository.findById(id);
		TechOnlineReviewEntity reviewEntity = null;
		if (option.isPresent()) {
			reviewEntity = option.get();
		} else {
			throw new BusinessException(ExceptionCodeEnum.TECH_ONLINE_REVIEW_NOT_EXIST);
		}

		reviewEntity.setOpenReview(true);
		reviewEntity.setUpdator(user);
		techOnlineReviewRepository.save(reviewEntity);
		Iterable<TechOnlineReviewTMTEntity> judgeslist = techOnlineReviewTMTRepository
				.findByTechOnlineReviewAndJudgesType(reviewEntity, 1);
		List<String> tmts = new ArrayList<>();
		judgeslist.forEach(t -> {
			if (Boolean.TRUE.equals(t.getIsParticipate())) {
				tmts.add(t.getTmtUser());
			}
		});
		// 发送通知给出席会议的评委
		if (CollUtil.isNotEmpty(tmts)) {
			techOnlineReviewSchedule.pushMessageToTmt(1, reviewEntity, tmts);
		}
		// 通知所有旁听人员
		judgeslist = techOnlineReviewTMTRepository.findByTechOnlineReviewAndJudgesType(reviewEntity, 2);
		List<String> judges = new ArrayList<>();
		judgeslist.forEach(t -> judges.add(t.getTmtUser()));
		if (CollUtil.isNotEmpty(judges)) {
			techOnlineReviewSchedule.pushMessageToTmt(2, reviewEntity, judges);
		}
		return null;
	}

	@Override
	public Object getRelevantInfoById(Long id) {
		ExpandCriteria<TechOnlineReviewRelevantEntity> c = new ExpandCriteria<>();
		c.add(Restrictions.eq("techOnlineReview.id", id, true));
		return techOnlineReviewRelevantRepository.findAll(c);
	}

	@Override
	public Page<TechOnlineReviewEntity> getList(String ptn, String reviewProcess, List<Integer> reviewType,
			Long workGroupId, Boolean isCross, String creator, Boolean isDisruptive, Boolean reviewResult,
			String proposalName, String subsidiary, String description, Integer techLevel, String roadMap,
			Date startDueDate, Date endDueDate, Integer pageNo, Integer pageSize, Set<String> responsible,
			Boolean isPlReview,Integer finalTechnologyLeadership,Integer finalTechnologyContribution) {
		ExpandCriteria<TechOnlineReviewEntity> c = new ExpandCriteria<>();
		c.add(Restrictions.like(TechOnlineReviewFileds.TECHPROJECTS_TECHPTN, ptn, true));
		c.add(Restrictions.eq(TechOnlineReviewFileds.WORKGROUP, workGroupId, true));
		c.add(Restrictions.eq(TechOnlineReviewFileds.ISCROSS, isCross, true));
		c.add(Restrictions.eq(TechOnlineReviewFileds.ISDISRUPTIVE, isDisruptive, true));
		// 评审中
		if (!ObjectUtils.isEmpty(reviewProcess)) {
			if (reviewProcess.endsWith("done")) {
				c.add(Restrictions.isNotNull(TechOnlineReviewFileds.REVIEWRESULT));
			} else {
				c.add(Restrictions.isNull(TechOnlineReviewFileds.REVIEWRESULT));
			}
		}
		c.add(Restrictions.eq(TechOnlineReviewFileds.REVIEWRESULT, reviewResult, true));
		c.add(Restrictions.like("creator", creator, true));
		c.add(Restrictions.like(TechOnlineReviewFileds.PROPOSALNAME, proposalName, true));
		c.add(Restrictions.like(TechOnlineReviewFileds.SUBSIDIARY, subsidiary, true));
		c.add(Restrictions.like(TechOnlineReviewFileds.DESCRIPTION, description, true));
		c.add(Restrictions.eq(TechOnlineReviewFileds.TECHLEVEL, techLevel, true));
		c.add(Restrictions.like(TechOnlineReviewFileds.ROADMAP, roadMap, true));
		c.add(Restrictions.gte(TechOnlineReviewFileds.DUEDATE, startDueDate, true));
		c.add(Restrictions.lte(TechOnlineReviewFileds.DUEDATE, endDueDate, true));
		c.add(Restrictions.in(TechOnlineReviewFileds.REVIEWTYPE, reviewType, true));
		// c.add(Restrictions.in("responsible",responsible,true));
		// c.add(Restrictions.eq("isPlReview",isPlReview,true));
		// 只取最顶上一层的数据
		c.add(Restrictions.isNull("parent"));
		List<Order> orders = new ArrayList<>();
		Order order = new Order(Sort.Direction.DESC, "createTime");
		orders.add(order);
		Pageable pageReq = PageRequest.of(pageNo - 1, pageSize, Sort.by(orders));
		Page<TechOnlineReviewEntity> page = techOnlineReviewRepository.findAll(c, pageReq);
		page.getContent().forEach(this::setChildren);
		return page;
	}

	/**
	 * 递归处理子节点数据
	 *
	 * @param t
	 */
	private void setChildren(TechOnlineReviewEntity t) {
		if (CollUtil.isEmpty(t.getChildren())) {
			t.setChildren(null);
		} else {
			t.getChildren().forEach(this::setChildren);
		}
	}

	/**
	 * 添加TMT信息
	 *
	 * @param vos
	 * @param relevantEntity
	 * @return
	 */
	private void addTmtReview(List<TechOnlineReviewTMTVo> vos, TechOnlineReviewRelevantEntity relevantEntity,
			WorkGroupEntity workGroup) {
		String user = ControllerCommonUtils.getHeader(PangooWebConstant.HEADER_NAME_USER_FULL_NAME);
		TechOnlineReviewEntity techOnlineReviewEntity = relevantEntity.getTechOnlineReview();
		List<Long> exists = new ArrayList<>();
		if (CollUtil.isNotEmpty(relevantEntity.getTmtInfos())) {
			exists = relevantEntity.getTmtInfos().stream().map(r -> r.getId()).collect(Collectors.toList());
		}
		if (CollUtil.isNotEmpty(vos)) {
			Set<TechOnlineReviewTMTEntity> tmtInfos = new HashSet<>();
			TechOnlineReviewTMTEntity entity = null;
			for (TechOnlineReviewTMTVo vo : vos) {
				// 同一个TMT成员在一个域不能重复添加
				if (null != vo.getId()) {
					Optional<TechOnlineReviewTMTEntity> op = techOnlineReviewTMTRepository.findById(vo.getId());
					if (op.isPresent()) {
						entity = op.get();
						exists.remove(vo.getId());
					} else {
						continue;
					}
				} else {
					entity = techOnlineReviewTMTRepository.findByTmtUserAndTechOnlineReview(vo.getTmtUser(),
							techOnlineReviewEntity);
					if (null == entity) {
						entity = new TechOnlineReviewTMTEntity();
						BeanUtils.copyProperties(vo, entity, "isParticipate", "isPassed", "reviewContent");
						entity.setCreator(user);
						entity.setTechOnlineRelevantReview(relevantEntity);
						entity.setTechOnlineReview(techOnlineReviewEntity);
						entity.setWorkGroup(workGroup);
					} else {
						log.error("TMT成员{}重复添加", vo.getTmtUser());
						throw new BusinessException(ExceptionCodeEnum.TECH_ONLINE_ITMT_REVIEWER_EXIST);
					}
				}
				// 正式参会人员:1，旁听人员:2
				entity.setJudgesType(vo.getJudgesType());
				tmtInfos.add(entity);
			}
			techOnlineReviewTMTRepository.saveAll(tmtInfos);
			relevantEntity.setTmtInfos(tmtInfos);
		} else {
			relevantEntity.setTmtInfos(null);
		}
		if (CollUtil.isNotEmpty(exists)) {
			// 删除多余的ID
			techOnlineReviewTMTRepository.deleteAllById(exists);
		}
	}

	/**
	 * TMT成员添加评审信息
	 */
	@Override
	public void tmtReview(TechOnlineReviewTMTVo vo) {
		String userId = ControllerCommonUtils.getHeader(PangooWebConstant.HEADER_NAME_USER_ID);
		String user = ControllerCommonUtils.getHeader(PangooWebConstant.HEADER_NAME_USER_FULL_NAME);
		Optional<TechOnlineReviewEntity> reviewOption = techOnlineReviewRepository.findById(vo.getTechOnlineReviewId());
		if (reviewOption.isPresent()) {
			TechOnlineReviewEntity reviewEntity = reviewOption.get();
			TechOnlineReviewTMTEntity entity = techOnlineReviewTMTRepository.findByTmtUserAndTechOnlineReview(userId,
					reviewEntity);
			if (null == entity) {
				throw new BusinessException(ExceptionCodeEnum.TECH_ONLINE_TMT_REVIEW_NOT_EXIST);
			}
			if (reviewEntity.getIsPlReview() != null) {
				Boolean itmtFlg = false;
				List<String> leaders = techOnlineReviewSchedule
						.getUserGroupLoginInfo(TechOnlineReviewSchedule.ITMT_LEADER);
				List<String> plleaders = techOnlineReviewSchedule
						.getUserGroupLoginInfo(TechOnlineReviewSchedule.PLITMT_LEADER);
				// ITMT Leader 或者ITMT 副Leader并且在PLTMT Leader审核之后可以参与评审反馈
				if ((CollUtil.isNotEmpty(leaders) && leaders.contains(userId))
						&& Boolean.FALSE.equals(reviewEntity.getIsPlReview())) {
					itmtFlg = true;
				}
				if (CollUtil.isNotEmpty(plleaders) && plleaders.contains(userId)
						&& Boolean.TRUE.equals(reviewEntity.getIsPlReview())) {
					itmtFlg = true;
				}
				if (Boolean.FALSE.equals(itmtFlg)) {
					throw new BusinessException(ExceptionCodeEnum.TECH_ONLINE_NOT_ITMT);
				}
				// 保存PLTMT Leader设置的领先度等属性
				entity.setTechnologyLeadership(vo.getTechnologyLeadership());
				entity.setTechnologyContribution(vo.getTechnologyContribution());
				entity.setIsPassed(vo.getIsPassed());
				entity.setReviewContent(vo.getReviewContent());
				entity.setCoreTechnology(vo.getCoreTechnology());
				techOnlineReviewTMTRepository.save(entity);
				techOnlineReviewRepository.save(reviewEntity);
				return;
			}
			if (null == reviewEntity.getOpenRelevant()) {
				entity.setIsParticipate(vo.getIsParticipate());
				// 重置状态
				entity.setIsPassed(null);
				techOnlineReviewTMTRepository.save(entity);
				return;
			}
			// '评委类型:正式参会人员:1，旁听人员:2
			if (entity.getJudgesType() == 2) {
				entity.setUpdator(user);
				// 旁听人员：只更新评审意见
				entity.setReviewContent(vo.getReviewContent());
				techOnlineReviewTMTRepository.save(entity);
			} else {
				if (null != reviewEntity.getOpenReview()) {
					if (Boolean.FALSE.equals(reviewEntity.getOpenReview())) {
						// 关闭反馈通道
						throw new BusinessException(ExceptionCodeEnum.TECH_ONLINE_ITMT_REVIEW_IS_END);
					}
					if (Boolean.TRUE.equals(entity.getIsParticipate())) {
						// 参会人员弃权
						if (Boolean.FALSE.equals(vo.getIsParticipate())) {
							entity.setIsParticipate(false);
							entity.setIsPassed(null);
							entity.setUpdator(user);
							// 评论
							entity.setReviewContent(vo.getReviewContent());
							techOnlineReviewTMTRepository.save(entity);
							return;
						}
						if (reviewEntity.getReviewType() == 1 && null != reviewEntity.getIsCross()) {
							if (null != vo.getIsCross()) {
								entity.setIsCross(vo.getIsCross());
							} else {
								throw new BusinessException(
										ExceptionCodeEnum.TECH_ONLINE_ITMT_REVIEW_RESULT_CROSSBREED_CATNOT_EMPTY);
							}
						}
						if (null == vo.getIsPassed()) {
							throw new BusinessException(ExceptionCodeEnum.TECH_ONLINE_ITMT_REVIEW_RESULT_CATNOT_EMPTY);
						}
						// 是否通过
						entity.setIsPassed(vo.getIsPassed());
						// 领先度，贡献度设置
						entity.setTechnologyLeadership(vo.getTechnologyLeadership());
						entity.setTechnologyContribution(vo.getTechnologyContribution());
						// 评论
						entity.setReviewContent(vo.getReviewContent());
						/** 潜在成本优势Cost Saving */
						entity.setCostSaving(vo.getCostSaving());
						/** 行业区别度Diffrentiation */
						entity.setDiffrentiation(vo.getDiffrentiation());
						/** 技术成熟度Maturity */
						entity.setMaturity(vo.getMaturity());
						/** 需求重要性Need */
						entity.setNeed(vo.getNeed());
						entity.setUpdator(user);
						/** 领先度和贡献度,核心技术 */
						entity.setTechnologyLeadership(vo.getTechnologyLeadership());
						entity.setTechnologyContribution(vo.getTechnologyContribution());
						entity.setCoreTechnology(vo.getCoreTechnology());
						techOnlineReviewTMTRepository.save(entity);
					} else {
						// 未参会评委显示“未参会”，失去表决权
						throw new BusinessException(ExceptionCodeEnum.TECH_ONLINE_ITMT_NOT_ATTEND);
					}
				} else {
					// 评审暂未开始，不能评审
					throw new BusinessException(ExceptionCodeEnum.TECH_ONLINE_ITMT_REVIEW_IS_NOT_BEGIN);
				}
			}
		} else {
			throw new BusinessException(ExceptionCodeEnum.TECH_ONLINE_REVIEW_NOT_EXIST);
		}
	}

	/**
	 * 更新TMT参会人员，只有参会的人员才会有评审权限
	 *
	 * @param vo
	 */
	@Override
	public void updateTmtAttendReviewMeeting(TechOnlineReviewAttendVo vo) {
		String user = ControllerCommonUtils.getHeader(PangooWebConstant.HEADER_NAME_USER_FULL_NAME);
		Optional<TechOnlineReviewEntity> reviewOption = techOnlineReviewRepository.findById(vo.getReviewId());
		if (reviewOption.isPresent()) {
			TechOnlineReviewEntity reviewEntity = reviewOption.get();
			if (null != reviewEntity.getOpenReview()) {
				// 评审已经开始，不能再添加或者删除评审人员
				throw new BusinessException(ExceptionCodeEnum.TECH_ONLINE_ITMT_REVIEW_ATTEND_END);
			}
			// 正式参会人员
			List<TechOnlineReviewTMTEntity> exitIds = techOnlineReviewTMTRepository
					.findByTechOnlineReviewAndJudgesType(reviewEntity, 1);
			// 已经存在的数据：删除评委信息的时候，对应状态重置
			exitIds.stream().filter(t -> Boolean.TRUE.equals(t.getIsParticipate())).forEach(t -> {
				if (!CollUtil.contains(vo.getIdList(), t.getId())) {
					t.setIsParticipate(null);
					t.setTechOnlineReview(reviewEntity);
					techOnlineReviewTMTRepository.save(t);
				}
			});
			Iterable<TechOnlineReviewTMTEntity> list = techOnlineReviewTMTRepository.findAllById(vo.getIdList());
			list.forEach(t -> {
				t.setIsParticipate(true);
				t.setUpdator(user);
				t.setIsPassed(null);
				t.setTechOnlineReview(reviewEntity);
				techOnlineReviewTMTRepository.save(t);
			});
		} else {
			throw new BusinessException(ExceptionCodeEnum.TECH_ONLINE_REVIEW_NOT_EXIST);
		}
	}

	/**
	 * 申请评审信息
	 *
	 * @param reviewId
	 * @return
	 */
	@Override
	public Object participateRateInfo(Long reviewId) {
		Optional<TechOnlineReviewEntity> reviewOption = techOnlineReviewRepository.findById(reviewId);
		if (!reviewOption.isPresent()) {
			throw new BusinessException(ExceptionCodeEnum.TECH_ONLINE_REVIEW_NOT_EXIST);
		}
		List<TechOnlineReviewTMTEntity> list = techOnlineReviewTMTRepository.findByTechOnlineReview(reviewOption.get());
		if (CollUtil.isEmpty(list)) {
			return null;
		}
		Map<String, Object> mapinfo = new HashMap<>(16);
		Integer all = list.size();
		// 评审结果
		mapinfo.put("reviewResult", reviewOption.get().getReviewResult());
		// 总的TMT数量
		mapinfo.put("all", all);
		// 没有参会的人数
		Long noParticipate = list.stream().filter(a -> a.getJudgesType() == 1).filter(a -> a.getIsParticipate() == null)
				.count();
		mapinfo.put("noParticipate", noParticipate);
		// 没有参会的人员
		List<String> noParticipateUsers = list.stream().filter(a -> a.getJudgesType() == 1)
				.filter(a -> a.getIsParticipate() == null).map(t -> t.getTmtFullUser()).toList();
		mapinfo.put("noParticipateUsers", noParticipateUsers);

		// 弃权
		Long abstention = list.stream().filter(a -> a.getJudgesType() == 1)
				.filter(a -> Boolean.FALSE.equals(a.getIsParticipate())).count();
		mapinfo.put("abstention", abstention);
		// 弃权的人员
		List<String> abstentionUsers = list.stream().filter(a -> a.getJudgesType() == 1)
				.filter(a -> Boolean.FALSE.equals(a.getIsParticipate())).map(t -> t.getTmtFullUser()).toList();
		mapinfo.put("abstentionUsers", abstentionUsers);

		// 参会的人员：总数
		Long onParticipate = list.stream().filter(a -> a.getJudgesType() == 1)
				.filter(a -> Boolean.TRUE.equals(a.getIsParticipate())).count();
		mapinfo.put("onParticipate", onParticipate);
		// 参会的人员
		List<String> onParticipateUsers = list.stream().filter(a -> a.getJudgesType() == 1)
				.filter(a -> Boolean.TRUE.equals(a.getIsParticipate())).map(t -> t.getTmtFullUser()).toList();
		mapinfo.put("onParticipateUsers", onParticipateUsers);

		// 旁听人员
		List<String> auditUser = list.stream().filter(a -> a.getJudgesType() == 2).map(t -> t.getTmtFullUser())
				.toList();
		mapinfo.put("auditUser", auditUser);

		// 评审结果：1通过，0不通过
		// 通过的人员
		Long passed = list.stream().filter(a -> a.getJudgesType() == 1)
				.filter(a -> Boolean.TRUE.equals(a.getIsPassed())).count();
		mapinfo.put("passed", passed);
		List<String> passedUsers = list.stream().filter(a -> a.getJudgesType() == 1)
				.filter(a -> Boolean.TRUE.equals(a.getIsPassed())).map(t -> t.getTmtFullUser()).toList();
		mapinfo.put("passedUser", passedUsers);

		// 参会：不通过的人员总是
		Long disPassed = list.stream().filter(a -> a.getJudgesType() == 1)
				.filter(a -> Boolean.FALSE.equals(a.getIsPassed())).count();
		mapinfo.put("disPassed", disPassed);
		// 参会：不通过的人员
		List<String> disPassedUsers = list.stream().filter(a -> a.getJudgesType() == 1)
				.filter(a -> Boolean.FALSE.equals(a.getIsPassed())).map(t -> t.getTmtFullUser()).toList();
		mapinfo.put("disPassedUsers", disPassedUsers);
		// 是否跨越
		mapinfo.put("isCross", reviewOption.get().getIsCrossReview());
		// 颠覆性技术评审得分
		mapinfo.put("disruptiveScore", reviewOption.get().getDisruptiveReview());
		return mapinfo;
	}

	@Override
	public List<TechOnlineReviewTMTEntity> getTmtReviewList(Long id) {
		Optional<TechOnlineReviewEntity> reviewOption = techOnlineReviewRepository.findById(id);
		if (reviewOption.isPresent()) {
			return techOnlineReviewTMTRepository.findByTechOnlineReview(reviewOption.get());
		} else {
			throw new BusinessException(ExceptionCodeEnum.TECH_ONLINE_REVIEW_NOT_EXIST);
		}
	}

	@Override
	public void pushMail(Long id) {
		Optional<TechOnlineReviewEntity> option = techOnlineReviewRepository.findById(id);
		// 邮件通知ITMT成员参与反馈
		try {
			if (option.isPresent()) {
				techOnlineReviewSchedule.technicalProposal(option.get());
			}
		} catch (Exception e) {
			log.error("{}", e);
		}
	}

	/**
	 * 获取历史评审记录：包含
	 *
	 * @param reviewType
	 * @param techId
	 *            PTN编号
	 * @return
	 */
	@Override
	public List<TechOnlineReviewEntity> getHistoryList(Integer reviewType, List<Long> techId) {
		ExpandCriteria<TechOnlineReviewEntity> c = new ExpandCriteria<>();
		c.add(Restrictions.in(TechOnlineReviewFileds.TECHPROJECTS, techId, true));
		c.add(Restrictions.eq(TechOnlineReviewFileds.REVIEWTYPE, reviewType, true));
		// 评审结果为通过的
		c.add(Restrictions.eq(TechOnlineReviewFileds.REVIEWRESULT, true, true));
		return techOnlineReviewRepository.findAll(c);
	}

	@Override
	public List<JSONObject> countExpertRviewListTmtInfo() {
		List<Object[]> list = techOnlineReviewTMTRepository.countExpertReviewListTmtInfo();
		List<JSONObject> listResult = list.stream().map(item -> {
			// isParticipate：null-未参会，
			// isParticipate：false-参会弃权评审，
			// isParticipate： true-参会
			JSONObject object = new JSONObject();
			object.put("count", item[0]);
			object.put("tmtfulluser", item[1]);
			object.put("participate", item[2]);
			object.put("reviewYear", item[3]);
			object.put("yearQuarter", item[4]);
			// isParticipate不为null且 isPassed为null，参会弃权
			object.put("isPassed", item[5]);
			return object;
		}).toList();
		Map<Object, List<JSONObject>> groupByUser = listResult.stream()
				.collect(Collectors.groupingBy(item -> item.get("tmtfulluser")));
		List<JSONObject> result = new ArrayList<>();
		groupByUser.forEach((key, value) -> {
			Map<String, JSONObject> infoMap = new HashMap<>(16);
			value.forEach(o -> {
				String timeKey = o.get("reviewYear") + "" + o.get("yearQuarter");
				JSONObject data = null;
				if (infoMap.containsKey(timeKey)) {
					data = infoMap.get(timeKey);
				} else {
					data = new JSONObject();
					infoMap.put(timeKey, data);
					data.put("tmtfulluser", key);
					data.put("reviewYear", o.get("reviewYear"));
					data.put("yearQuarter", o.get("yearQuarter"));
					// 参会
					data.put("isParticipate", 0);
					// 弃权
					data.put("abstention", 0);
					// 未参会弃权
					data.put("noParticipate", 0);
				}
				// isParticipate不为null且 isPassed为null，参会弃权
				if (null == o.getBoolean("participate")) {
					data.put("noParticipate", data.getIntValue("noParticipate") + o.getIntValue("count"));
				} else {
					if (null == o.getBoolean("isPassed")) {
						data.put("abstention", data.getIntValue("abstention") + o.getIntValue("count"));
					} else {
						data.put("isParticipate", data.getIntValue("isParticipate") + o.getIntValue("count"));
					}
				}
			});
			result.addAll(infoMap.values());
		});
		return result;
	}

	@Override
	public Object monthlyProgressChart(String year, Integer reviewType) {
		ExpandCriteria<TechOnlineReviewEntity> c = new ExpandCriteria<>();
		if (!ObjectUtils.isEmpty(year)) {
			Date date = DateUtil.parse(year, "yyyy");
			c.add(Restrictions.gte("dueDate", DateUtil.beginOfYear(date), true));
			c.add(Restrictions.lte("dueDate", DateUtil.endOfYear(date), true));
		}
		c.add(Restrictions.eq(TechOnlineReviewFileds.REVIEWTYPE, reviewType, true));
		// 评审完成的
		c.add(Restrictions.isNotNull(TechOnlineReviewFileds.REVIEWRESULT));
		List<TechOnlineReviewEntity> list = techOnlineReviewRepository.findAll(c);
		// 评审类型:技术规划评审:1，技术定义与研究任务书评审:2，技术成果交付评审:3
		Set<TechOnlineReviewMonthVo> result = new HashSet<>();
		list.stream().forEach(t -> {
			String month = DateUtil.format(t.getDueDate(), "yyyy-MM");
			Integer type = t.getReviewType();
			Boolean isPass = t.getReviewResult();
			Optional<TechOnlineReviewMonthVo> object = result.stream().filter(
					r -> r.getYearMonth().equals(month) && r.getType().equals(type) && r.getIsPassed().equals(isPass))
					.findFirst();
			TechOnlineReviewMonthVo vo;
			if (object.isPresent()) {
				vo = object.get();
				vo.setCount(vo.getCount() + 1);
			} else {
				vo = new TechOnlineReviewMonthVo();
				vo.setCount(1);
				vo.setIsPassed(isPass);
				vo.setType(type);
				vo.setYearMonth(month);
			}
			result.add(vo);
		});
		return result;
	}

}
